package system.task.utils;

import lombok.SneakyThrows;
import org.quartz.*;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;
import system.entity.SysTaskJob;
import system.entity.SysTaskLog;
import system.task.Task;
import system.task.defined.TaskInstruction;
import system.task.errors.TaskException;
import system.task.vo.SysTaskRunning;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;

/**
 * 任务工具
 */
public class TaskUtil {

    /**
     * 组名称
     */
    public final static String GROUP_NAME = "DEFAULT";
    /**
     * 任务加载器
     */
    public final static String TASK_LOADER_NAME = "TaskLoader";
    /**
     * 规则
     */
    public final static String TASK_RULE_NAME = "___RULE";

    /**
     * 任务信息体
     */
    public final static String TASK_TASK_NAME = "___TASK";

    /**
     * 任务数据实体
     */
    public final static String TASK_TASK_JOB_NAME = "___TASK_JOB";

    /**
     * 任务数据实体
     */
    public final static String TASK_TASK_RUN_HOST = "___TASK_HOST";

    /**
     * 任务数据实体
     */
    public final static String TASK_TASK_RUN_ENV = "___TASK_ENV";

    /**
     * 手动触发标志
     */
    public final static String TASK_TRIGGER_MANUAL = "___TRIGGER_MANUAL";

    /**
     * 参数
     */
    public final static String TASK_PARAM_NAME = "___PARAM";

    /**
     * 运行ID
     */
    public final static String TASK_RUN_ID_NAME = "___RUN_ID";
    /**
     * 运行日志路径
     */
    public final static String TASK_RUN_LOG_NAME = "___RUN_LOG";
    /**
     * 运行结果
     */
    public final static String TASK_RUN_RESULT = "___RUN_RESULT";

    /**
     * 最大时间
     */
    public final static Date MAX_TIME = Date.from(ZonedDateTime.of(9999, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC")).toInstant());

    /**
     * 移除已过期
     *
     * @param jobList
     */
    public static void removeExpires(List<SysTaskJob> jobList) {
        Date now = new Date();
        jobList.removeIf(job -> (job.getExpireTime() != null && job.getExpireTime().getTime() < now.getTime()));
    }

    /**
     * 移除未开始
     *
     * @param jobList
     */
    public static void notStart(List<SysTaskJob> jobList) {
        Date now = new Date();
        jobList.removeIf(job -> (job.getStartTime() != null && job.getStartTime().getTime() > now.getTime()));
    }

    /**
     * 清除最大时间值
     *
     * @param job
     */
    public static void cleanJobMaxTime(SysTaskJob job) {
        if (TaskUtil.MAX_TIME.equals(job.getStartTime())) job.setStartTime(null);
//        if (TaskUtil.MAX_TIME.equals(job.getNextTime())) job.setNextTime(null);
//        if (TaskUtil.MAX_TIME.equals(job.getRunTimeStart())) job.setRunTimeStart(null);
//        if (TaskUtil.MAX_TIME.equals(job.getRunTimeEnd())) job.setRunTimeEnd(null);
        if (TaskUtil.MAX_TIME.equals(job.getExpireTime())) job.setExpireTime(null);
//        if (TaskUtil.MAX_TIME.equals(job.getLoadTime())) job.setLoadTime(null);
    }

    /**
     * 清除最大时间值
     *
     * @param list
     */
    public static void cleanJobMaxTime(List<SysTaskJob> list) {
        if (list != null) {
            for (SysTaskJob job : list) {
                cleanJobMaxTime(job);
            }
        }
    }

    public static void cleanLogMaxTime(SysTaskLog job) {
        if (TaskUtil.MAX_TIME.equals(job.getRunTimeStart())) job.setRunTimeStart(null);
        if (TaskUtil.MAX_TIME.equals(job.getRunTimeEnd())) job.setRunTimeEnd(null);
    }

    public static void cleanLogMaxTime(List<SysTaskLog> list) {
        if (list != null) {
            for (SysTaskLog job : list) {
                cleanLogMaxTime(job);
            }
        }
    }

    /**
     * 启动一个后台线程
     *
     * @param runnable
     * @return
     */
    public static Thread startDaemonThread(Runnable runnable) {
        Thread thread = new Thread(runnable);
        thread.setDaemon(true);
        thread.start();
        return thread;
    }

    /**
     * 获取本机主机名称
     *
     * @return
     */
    @SneakyThrows
    public static String hostname() {
        String hostName = "";
        try {
            InetAddress localHost = InetAddress.getLocalHost();
            if (localHost != null) {
                hostName = localHost.getHostName();
            }
        } catch (UnknownHostException e) {
            hostName = "";
        }
        return hostName;
    }

    /**
     * 填充任务运行信息
     *
     * @param info
     * @param task
     * @param running
     */
    public static void fillRunning(SysTaskJob info, Task task, SysTaskRunning running) {
        BeanUtils.copyProperties(info, running);
        running.setRunRemarks(task.getRunRemarks());
        running.setRunStatus(task.getRunStatus());
        running.setRunTimeStart(task.getRunTimeStart());
        running.setRunTimeEnd(task.getRunTimeEnd());
        running.setNextTime(task.getNextTime());
        running.setScheTime(task.getScheTime());
        running.setPrevTime(task.getPrevTime());
        running.setLoadTime(task.getLoadTime());
    }

    public static Trigger parseTrigger(Task task, TriggerKey triggerKey) throws TaskException {
        Trigger trigger = null;
        TaskInstruction instruction = task.getInstruction();

        //
        if (task.getSchedule() == null) task.setSchedule("");
        task.setSchedule(task.getSchedule().trim());

        //
        Date startTime = null;
        if (task.getStartTime() != null && task.getStartTime().getTime() > System.currentTimeMillis()) {
            startTime = task.getStartTime();
        } else {
            startTime = new Date(System.currentTimeMillis() + 60 * 1000L);
        }

        //
        Date endTime = null;
        if (task.getExpireTime() != null && task.getExpireTime().getTime() > System.currentTimeMillis()) {
            if (task.getStartTime() == null) {
                endTime = task.getExpireTime();
            } else if (task.getExpireTime().getTime() > task.getStartTime().getTime()) {
                endTime = task.getExpireTime();
            }
        }

        //
        if (task.getSchedule().startsWith("INTERVAL:")) {
            String interval = task.getSchedule().substring("INTERVAL:".length());
            int sec = 0;
            try {
                sec = Integer.parseInt(interval);
            } catch (Exception exception) {
                throw new TaskException("Interval invalid " + interval + " of " + task.getName(), exception);
            }
            if (sec < 1) {
                throw new TaskException("Interval invalid " + interval + ", value must >= 1 " + task.getName());
            }
            SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.repeatSecondlyForever(sec);
            if (TaskInstruction.NEXT.equals(instruction)) {
                scheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
            } else if (TaskInstruction.NOW.equals(instruction)) {
                scheduleBuilder.withMisfireHandlingInstructionFireNow();
            } else {
                scheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
            }
            trigger = TriggerBuilder.newTrigger()
                    .withIdentity(triggerKey)
                    .withSchedule(scheduleBuilder)
                    .startAt(startTime)
                    .endAt(endTime)
                    .forJob(triggerKey.getName(), triggerKey.getGroup())
                    .build();
        } else if (StringUtils.hasText(task.getSchedule())) {
            // } else if (task.getSchedule().startsWith("CRON:")) {
            String cron;
            if (task.getSchedule().startsWith("CRON:"))
                cron = task.getSchedule().substring("CRON:".length());
            else
                cron = task.getSchedule();
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            if (TaskInstruction.NEXT.equals(instruction)) {
                scheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
            } else if (TaskInstruction.NOW.equals(instruction)) {
                scheduleBuilder.withMisfireHandlingInstructionFireAndProceed();
            } else {
                scheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires();
            }
            trigger = TriggerBuilder.newTrigger()
                    .withIdentity(triggerKey)
                    .withSchedule(scheduleBuilder)
                    .startAt(startTime)
                    .endAt(endTime)
                    .forJob(triggerKey.getName(), triggerKey.getGroup())
                    .build();
        }
        //
        return trigger;
    }

}